<?php

namespace app\common\helper;
use think\Exception;

/**
 * 进程控制类
 * Class Pcntl
 */
class Pcntl {

    public static $count = 3;            //工作进程数
    public static $pids = array();       //工作进程pid
    public static $workerExec;           //工作进程执行内容
    public static $pidFile = '/tmp/pcntl.pid';   //pid文件
    public static $daemonize = false;    //守护进程化
    public static $monitorExec;  //监控中附加执行内容

//    public static $onWorkerExecSuccess;

    public static function start() {
        self::init();
        self::forkWorkers();
        self::initSignal();
        self::monitor();
    }

    /**
     * 初始化
     */
    public static function init() {
        
        //解析命令
        self::parseCommand();

        //守护进程时，派生进程 脱离终端
        if (self::$daemonize) {
            umask(0);
            $pid = pcntl_fork();

            if (-1 === $pid) {
                throw new Exception('fork fail');
            } elseif ($pid > 0) {
                exit;
            }

            if (-1 === posix_setsid()) {
                throw new Exception("setsid fail");
            }
        }

        $masterPid = getmypid();
        self::log("master[" . $masterPid . '] 初始化');
        if (file_put_contents(self::$pidFile, $masterPid) === false) {
            throw new Exception('无法保存pid');
        }
    }

    /**
     * fork全部工作进程
     */
    public static function forkWorkers() {
        for ($i = 0; $i < self::$count; $i++) {
            self::forkWorker($i);
        }
    }

    /**
     * fork一个工作进程
     */
    public static function forkWorker($i) {
        $pid = pcntl_fork();
        if ($pid > 0) {
            self::$pids[] = $pid;
            self::log('worker[' . $pid . '] 已生成');
        } elseif ($pid == 0) {
            //守护进程时，关闭所有输出
            if (self::$daemonize) {
                global $STDOUT, $STDERR;
                fclose(STDOUT);
                fclose(STDERR);
                $STDOUT = fopen('/dev/null', "rw+");
                $STDERR = fopen('/dev/null', "rw+");
            }
            call_user_func_array(self::$workerExec, array($i));
            exit;
        } else {
            //todo: 记录失败
        }
    }

    /**
     * 监控
     */
    public static function monitor() {
        while (true) {
            $pid = pcntl_waitpid(-1, $status, WUNTRACED|WNOHANG);
            pcntl_signal_dispatch();
            if ($pid) {
                self::log("worker[" . $pid . "] 结束运行");
                $index = array_search($pid, self::$pids);
                unset(self::$pids[$index]);
            }

            //填补worker进程
//            if (count(self::$pids) < self::$count) {
//                self::forkWorker();
//            }
//            
//            if(is_callable(self::$monitorExec)){
//                call_user_func_array(self::$monitorExec,array());
//            }
            
            usleep(1000000);
        }
    }

    /**
     * 设定进程数
     * @param $count
     */
    public static function setCount($count) {
        self::$count = $count;
    }

    /**
     * @param mixed $monitorExec
     */
    public static function setMonitorExec($monitorExec) {
        self::$monitorExec = $monitorExec;
    }

    /**
     * 设定工作进程执行内容
     * @param $func
     */
    public static function setWorkerExec($func) {
        self::$workerExec = $func;
    }

    /**
     * 初始化信号
     */
    public static function initSignal() {
        pcntl_signal(SIGINT, array('self', 'signalHandler'));
    }

    /**
     * 信号处理
     * @param $sig
     */
    public static function signalHandler($sig) {
        switch ($sig) {
            case SIGINT:
                self::stopAll();
                self::log('master[' . getmypid() . '] 已停止');
                exit;
            default:
        }
    }

    /**
     * 停止所有工作进程
     */
    public static function stopAll() {
        shell_exec('sh ' . dirname(dirname(__FILE__)) . '/scripts/rkill.sh ' . getmypid() . ' > /dev/null 2>&1');
    }

    public static function setPidFile($file) {
        self::$pidFile = $file;
    }

    /**
     * 设定是否是守护进程模式
     * @param bool $isDaemon
     */
    public static function isDaemon($isDaemon = true) {
        self::$daemonize = $isDaemon;
    }

    /**
     * 记录日志
     */
    public static function log($msg) {
//        $debugInfo = debug_backtrace();
//        $logMsg = $debugInfo[0]['file'].':'.$debugInfo[0]['line'].'  '.date('Y-m-d H:i:s') . ' ' . 'pid:'. posix_getpid().'  '.$msg;
        
        //非守护进程时输出消息信息
        if (!self::$daemonize) {
//            echo $logMsg . PHP_EOL;
        }
        
//        Logger::log($logMsg);
    }

    /**
     * 解析命令
     */
    public static function parseCommand(){

        //使用全局变量argv
        global $argv;

        $arg1 = isset($argv[1])?$argv[1]:'';
        $arg2 = isset($argv[2])?$argv[2]:'';

        if($arg2==='-d'){
            self::isDaemon();
        }

        switch($arg1){
            case 'stop':
                if (is_file(self::$pidFile)) {
                    $pid = file_get_contents(self::$pidFile);
                    $isAlived = $pid && posix_kill($pid, 0);
                    if ($isAlived) {
                        shell_exec('sh ' . dirname(dirname(__FILE__)) . '/scripts/rkill.sh ' . $pid . ' > /dev/null 2>&1');
                        self::log('服务已停止');
                        exit;
                    }else{
                        self::log('无法指定该操作: 当前没有启动任何服务');
                        exit;
                    }
                }
                break;
            case 'start':
            default:
                //检查是否已经启动
                if (is_file(self::$pidFile)) {
                    $pid = file_get_contents(self::$pidFile);
                    $isAlived = $pid && posix_kill($pid, 0);
                    if ($isAlived) {
                        self::log('pid[' . $pid . ']已经在运行中');
                        exit;
                    }
                }
        }
    }

}